#ifndef ROUTING1_H
#define ROUTING1_H

#include<QtCore>
#include <QtWidgets>
#include "cell.h"

class Routing1
{
    QPlainTextEdit* debug;
    Cell *cell;

    bool isInterBandNet(QString net);
    bool isInternNet(QString netName, QList<Transistor *> *transistors);

    Metal *createContact(Metal *m);
    Metal *getCollidingMetal(Metal *m, QList<Metal *> *items, qreal spacingRule);
    Metal* insertHorizontalM1(QList<Metal *> *contacts, qreal initialY, QString direction);
    Metal* insertHorizontalPoly(Transistor *t1, Transistor *t2, QList<Metal *> *poItems, qreal initialY, QString direction);
    Metal *insertHorizontalM1(Metal *m2, QList<Transistor *> *transistors);
    Metal *insertVerticalM2(QString net, qreal middle, QString direction);

    QStringList *getInterBandNets();
    QStringList *getInternNets(QList<Transistor *> *transistors);

    qreal getContactPosition(QString net, QList<Transistor*> *transistors, Transistor* t);
    qreal getFirstXOcurrency(QString net, QList<Transistor *> *transistors, bool compareDiff, bool compareGate);
    qreal getFirstXOcurrency(QString net, bool compareDiff, bool compareGate);
    qreal getLastXOcurrency(QString net, bool compareDiff, bool compareGate);
    qreal getLastXOcurrency(QString net, QList<Transistor *> *transistors, bool compareDiff, bool compareGate);

    QList<Metal *>* insertContacts(QString net, QList<Transistor *> *transistors);

    void bandRouting();
    void deflateMetal(Metal *m1, qreal rule, qreal p);
    void fixContactsPosition(QList<Metal *> *contacts, Metal *m1);
    void inflateMetal(Metal *m1, qreal rule, qreal p);
    void insertHorizontalM1Nets(QStringList *internNets, QList<Transistor *> *transistors);
    void insertHorizontalM1Net(QString net, QList<Transistor *> *transistors);
    void insertM1ToContacts();
    Metal *insertVerticalPoly(Transistor *t, Metal *po);
    void insertVerticalPoly(Transistor *pt, Transistor *nt);
    void internRouting();
    void internRouting(QList<Transistor *> *transistors);
    void resetRouting();

    void routeBandNets(QStringList *interBandNets);
    void routeBandNet(QString net);
    void routeBandToGate();
    void routeBandToGate(QStringList *bandToGateNets, QList<Transistor *> *transistors);
    void routeGates();
    void routeGatesBySide();
    void routeGatesBySide(QList<Transistor *> *transistors);
    void routeVccGnd();
    void routeVccGnd(QString net, QList<Transistor *> *transistors);
    void routeVerticalGates(QList<Metal *> *m1Items);
    void sortByWide(QStringList *internNets);
    void sortByWidth(QStringList *internNets);
    void sortByXPos(QList<Metal *> *m);

public:
    Routing1(Cell *cell,QPlainTextEdit* debug);
    void route();

    qreal getNetWidth(QString net);

    void routeBandToGate(QList<Transistor *> *transistors);
    QStringList *getDiffToGateNets(QList<Transistor *> *transistors);
    void insertDiffToContacts();
    void stretchM1(Metal *m1, Metal *co);
    void cutCollidingArea(Metal *m1, QString direction);
    void routeGatesBySide(QList<Transistor *> *transistors, int distance);
    void resetFlags();
    bool fixMetalPosition(Metal *m, QList<Metal *> *items, qreal spacingRule, QString direction);
    QList<Metal *> *getPoItems();
    QList<Metal *> *getPoItems(QList<Transistor *> *transistors);
    void routeGates(QList<Transistor *> *transistors);
    Metal *createPoContact(Transistor *t);
    void routeGatesWithHorizontalM1();
    QList<QStringList *> *getGateConflicts(QStringList *gateNets);
    QStringList *getGateConflicts(QString gateNet);
    QStringList *getGatesM1Conflicts(QString gateNet);
    bool hasIntersection(QString gateNet1, QString gateNet2);
    QStringList *getOppositeGateNets(QString gateNet);
    QStringList *getOppositeGateNets(QString gateNet, QList<Transistor *> *transistors);
    Transistor *getOppositeTransistor(Transistor *t);
    int evaluateSolution(QStringList *solution, QStringList *gateNets);
    QList<Metal *> *insertGateContacts(QString gateNet, QList<Transistor *> *transistors);
    void fitToBestPlace(QList<Metal *> *m1Items);
    int countUnsolvedConflicts(QList<Metal *> *m1Items);
    int countUnsolvedConflicts(QList<Metal *> *m1Items, QList<Transistor *> *transistors);
    Metal *getMetalNet(QString net, QList<Metal *> *metalItems);
    QList<Metal *> *insertM1GateItems(QStringList *solution, QStringList *gateNets);
    QList<Metal *> *insertGateContacts(QString gateNet);
    bool hasOppositeGateConflict(Transistor *t, QList<Metal *> *m1Items);
    bool hasSelfGateConflict(Transistor *t, QList<Metal *> *m1Items);
    QStringList *getNextSolution(QStringList *curSolution, QList<QStringList *> *conflicts, QStringList* gateNets);
    qreal getSmallestTranslatePossible(QList<Metal *> *m1Items, Metal **startMetal);
    bool canSwap(QString net1, QString net2, QList<QStringList *> *conflicts, QStringList* gateNets);
    qreal getMinSelfGateConflict(Metal *m1, qreal d);
    void translateGateM1(Metal *m1, QList<Metal *> *m1Items, qreal d);
    int countOutsideM1(QList<Metal *> *m1Items);
    QList<qreal> *getYPositions(QList<Metal *> *m1Items);
    void setYPosition(QList<Metal *> *m1Items, QList<qreal> *yPositions);
    void mergeIdenticalPo();
    void mergeParallelPo();
    Metal *getParallelPo(Metal *po);
    void mergeSequencialPo();
    Metal *getSequencialPo(Metal *po);
    Transistor *getGreaterTransistor(QString net, Transistor *t, QList<Transistor *> *transistors);
    QStringList *getDiffToGateNets();
    void routeBandToGate(QString net, QList<Transistor *> *transistors);
    void routeVerticalGates(QList<Transistor *> *transistors, QList<Metal *> *m1Items);
    void routeVerticalGate(Transistor *t, QList<Metal *> *m1Items);
    Metal *getGateM1(QString net, QList<Metal *> *m1Items);
    QList<Transistor *> *getConflictedTransistors();
    QList<Transistor *> *getConflictedTransistors(QList<Transistor *> *transistors);
    QList<Transistor *> *getComplement(QList<Transistor *> *transistors);
    QList<Transistor *> *getComplement(QList<Transistor *> *transistors, QList<Transistor *> *cellTransistors);
    QList<Transistor *> *getTransistorsList(Transistor *t);
    bool routeConflictedTransistor(Transistor *t);
    void routeConflictedTransistors(QList<Transistor *> *conflictedTransistors);
    Transistor *routeBySibling(Transistor *t);
    void insertGap(Transistor *t);
    Transistor *getSibling(QList<Transistor *> *transistors, Transistor *t, int step);
    Transistor *getLeftSibling(QList<Transistor *> *transistors, Transistor *t);
    Transistor *getRightSibling(QList<Transistor *> *transistors, Transistor *t);
    Transistor *getLessBlockingClosestSibling(Transistor *t);
    void sortByLessBlockingClosestSibling(QList<Transistor *> *transistors);
    void solveGateWithoutRoutedSibling(QList<Transistor *> *conflictedTransistors);
    QList<Transistor *> *getTransistorsWithoutRoutedSibling(QList<Transistor *> *conflictedTransistors);
    Transistor *getTransistorByGate(int initialIndex, Transistor *t, int step, bool byPosIndex);
    Transistor *getMostCenteredTransistor(Transistor *t);
    void removeTransistorsWithSimilarGate(QList<Transistor *> *transistorsWithoutRoutedSibling, Transistor *t);
    QList<Transistor *> *getOppositeTransistorsList(Transistor *t);
    bool hasRoutedSibling(Transistor *t);
    int countBlocked(Transistor *t1, Transistor *t2);
    void fixPosIndexDueToGap(Transistor *t);
    void fixGateM1(Metal *m);
    void removeContacts(Metal *m);
    void fixGateM1DueToGap();
    bool fixOutsideGates();
    QList<Metal *> *getGatesM1();
    QList<QList<Metal *> *> *getPossibleSolutionsForShorteningGateM1();
    QList<Metal *> *generatePossibleSolutionsForShorteningGateM1(Metal *m);
    QList<Metal *> *getBestSolutionForShorteningGateM1();
    QList<Metal *> *createSolutionForShorteningGateM1(QList<int> *combination, QList<QList<Metal *> *> *possibleSolutions);
    void sortByYPos(QList<Metal *> *m);
    void condenseGateM1(QList<Metal *> *m1Items);
    void getNextCombination(QList<int> *curCombination, QList<QList<Metal *> *> *possibleSolutions);
    int evaluateSolutionForShoteningGateM1(QList<Metal *> *solution, QList<Metal *> *polyItems);
    QList<Metal *> *routeSolutionWithPoly(QList<Metal *> *gateM1Items);
    QList<Metal *> *routeBySide();
    QList<Metal *> *createPolySideConnections();
    QList<Metal *> *createPolySideConnections(QList<Transistor *> *transistors);
    Transistor *getTransistorByGate(int initialIndex, QString gateNet, int step, QList<Transistor *> *transistors);
    bool hasRoutedSiblingBetween(Transistor *t1, Transistor *t2);
    void sortByLessBlockingShorterPoly(QList<Metal *> *poItems);
    int countBlocked(Metal *po);
    QList<Metal *> *getContactsFromMetal(Metal *m);
    bool solveOutsideMetal(QList<Metal *> *solution);
    QList<Metal *> *insertVerticalPoly(Metal *po);
    bool insertNonConflictedPolyItems(QList<Metal *> *poToBeInserted);
    bool hasCollidingMetal(QList<Metal *> *poItems1, QList<Metal *> *poItems2);
    QList<Metal *> *getGateContacts(Metal *m);
    bool coverBothBands(Metal *m);
    bool coverAtLeastOneTransistor(Metal *m, QList<Transistor *> *transistors);
    void substituteGateM1(QList<Metal *> *curSolution);
    void releaseMemory(QList<Metal *> *items);
    void releaseMemory(QList<QList<Metal *> *> *listOfList);
    QList<Metal *> *clone(QList<Metal *> *list);
    void pushM1GatesUp(QList<Metal *> *m1Items);
    bool isLastCombination(QList<int> *curCombination, QList<QList<Metal *> *> *possibleSolutions);
    void fixHorizontalGatePoPosition(QList<Metal *> *poItems);
    int countVerticalGateConflicts(QList<Metal *> *polyItems);
    int countOutsideMetal(QList<Metal *> *items);
    int getFirstDiffContactIndex(QList<Metal *> *contacts);
    int getLastDiffContactIndex(QList<Metal *> *contacts);
    QList<Metal *> *getNonGateMetal(QList<Metal *> *items);
    void routeConflictedTransistors();
    void removeGateVerticalPo(Transistor *t);
    void setNotRouted(QList<Transistor *> *transistors);
    Transistor *getLessBlockingTransistorToRoute(QList<Transistor *> *transistors);
    QList<Metal *> *getTransistorPoItems();
    QList<Transistor *> *getAllTransistorSiblings(Transistor *t);
    int evaluateTransistorRouting(Transistor *t);
    int evaluateTransistorRouting(Transistor *t1, Transistor *t2);
    Transistor *getOppositeRightSibling(Transistor *t);
    Transistor *getOppositeLeftSibling(Transistor *t);
    Transistor *getGreaterTransistor(Transistor *t1, Transistor *t2);
    Transistor *getBestSiblingToRoute(Transistor *t);
    bool canBeRouted(Metal *hPo, QList<Metal *> *poItems, Transistor *t1, Transistor *t2);
    bool routeTransistors(Transistor *t1, Transistor *t2);
    Metal *getPoItem(Transistor *t);
    bool isOverOppositeTransistor(Transistor *t);
    void fixMetal2Height();
    void fixMetal2Height(Metal *m2);
    QList<Metal *> *replaceMetal2WithMetal1(Metal *m2, Metal *m1);
    void replaceMetal2WithMetal1(Metal *m2);
    void replaceMetal2WithMetal1();
    int countHorizontalPolyItems(QList<Metal *> *poItems);
    void fixGatePolyDueToGap();
    void movePolyIn();
    void movePolyIn(Metal *po);
    Transistor *getGreaterTransistor(QList<Transistor *> *transistors);
    void releaseMemory(Metal *m);
    void setPorpouse(QList<Metal *> *items, int porpouse);
    void fillNonGapSpaceWithDiff();
    void fillNonGapSpaceWithDiff(QList<Transistor *> *transistors);
    QString getLeftNet(Transistor *t);
    QString getRightNet(Transistor *t);
    void fillNonGapSpaceWithDiff(Transistor *left, Transistor *right);
    void reRoute();
    void condenseOutsidePoly();
    void condenseOutsidePoly(QList<Metal *> *outSidePoly, QString band);
    void fixVerticalPolyItems(Metal *po, QString band);
    QList<Metal *> *getOutSidePoly(QString band);
    void insertGatePolyContact(Metal *po);
    void insertGatePolyContacts();
    QList<Metal *> *getHigherMetals(Metal *m1, QList<Metal *> *m1Items);
    void solveDesignRulesConflicts();
    void solvePolyDesignRulesConflicts();
    qreal translateMetal(Metal *m, QList<Metal *> *items, qreal dx, qreal rule, QString direction);
    void translateTransistors(Transistor *t, qreal dx);
    void translateTransistors(QList<Transistor *> *transistors, int initialIndex, qreal dx);
};

#endif // ROUTING1_H
